<?php
/**
 * Eren Framework
 * 
 * The adapter of DB operation.
 * You can develop a DB class like Eren_DB_MySQL(must inherit from Eren_DB_Abstract) and use it through this adapter.
 * 
 * @category System
 * @author Manu Soros Leu
 * @copyright manusoros@gmail.com
 */

class Eren_DB_Adapter
{
    
    /**
     * The instance of DB class like 'Eren_DB_MySQL' 
     *
     * @var Eren_DB_Abstract
     */
    protected $_DB = NULL;
    
    /**
     * The fields of DB Talbe, used for SELECT operation
     * 
     * @var string
     */
    protected $_fields = NULL;
    
    /**
     * The name of DB Table of operation
     * 
     * @var string
     */
    protected $_table = NULL;
    
    /**
     * The where condition of opreation
     * 
     * @var string
     */
    protected $_where = NULL;
    
    /**
     * The limit condition of opreation
     * 
     * @var string
     */
    protected $_limit = NULL;
    
    /**
     * The order condition of operation
     * 
     * @var string
     */
    protected $_order = NULL;
    
    /**
     * The SQL for DB to execute
     * 
     * @var string
     */
    protected $_SQL = NULL;
    
    /**
     * The white list Operators of WHERE condition
     * 
     * @var array
     */
    protected $_op = array(
    '='        =>true,
    '<'        =>true,
    '<='       =>true,
    '>'        =>true,
    '>='       =>true,
    '!='       =>true,
    '<>'       =>true,
    'LIKE'     =>true
    );
    
    /**
     * Constuct of this class
     * 
     * @param  Eren_DB_Abstract  $DB  Instance of DB class which inherit from Eren_DB_Abstract
     * @throws Eren_DB_Exception
     */
    public function __construct($DB)
    {
        if(!Eren_Validator::isBaseClass($DB, 'Eren_DB_Abstract')){
            throw new Eren_DB_Exception('The param of construct of Eren_DB_Adapter must inherit from Eren_DB_Abstract');
        }
        $this->_DB = $DB;
    }
    
    /**
     * Init every param prevent from influencing each operation 
     * 
     */
    private function _init()
    {
        $this->_fields    = NULL;
        $this->_table     = NULL;
        $this->_where     = NULL;
        $this->_limit     = NULL;
        $this->_order     = NULL;
    }
    
    /**
     * The name of DB to operate 
     *
     * @param string $DBName
     * @throws Eren_DB_Exception
     * @return this
     */
    public function DB($DBName)
    {
        if(is_array($DBName) || empty($DBName)){
            throw new Eren_DB_Exception("The param of DB must be string and cant be null");
        }
        $this->_DB->DB($DBName);
        return $this;
    }
    
    /**
     * The name of Table to operate 
     *
     * @param string $table
     * @throws Eren_DB_Exception
     * @return this
     */
    public function table($table)
    {
        if(is_array($table) || empty($table)){
            throw new Eren_DB_Exception("The param of table must be string and cant be null");
        }
        $this->_table = $table;
        return $this;
    }
    
    /**
     * Execute SELECT operation 
     *
     * @param array|string $fields
     * @throws Eren_DB_Exception
     * @return array
     */
    public function select($fileds = '*')
    {
        if(!$this->_table){
            throw new Eren_DB_Exception("Table name cant be NULL");
        }
        
        /*
         * Init FIELDS for SELECT
         */
        if(empty($fileds)){
            $this->_fields = '*';
        }else if(!is_array($fileds)){
            $this->_fields = $fileds;
        }else{
            $this->_fields = implode(',', $fileds);
        }
        $this->_SQL = "SELECT {$this->_fields} FROM {$this->_table} ";
        
        /*
         * Add WHERE condition
         */
        if($this->_where){
            $this->_SQL .= "WHERE {$this->_where}";
        }
        
        /*
         * Add ORDER conditon
         */
        if($this->_order){
            $this->_SQL .= "ORDER BY {$this->_order}";
        }
        
        /*
         * Add LIMIT conditon
         */
        if($this->_limit){
            $this->_SQL .="LIMIT {$this->_limit}";
        }
        $this->_init();
        return $this->_DB->select($this->_SQL);
    }
    
    /**
     * Execute INSERT operation 
     *
     * @param array $fields  The key is the field of table, the value is the value you want to insert 
     * @throws Eren_DB_Exception
     * @return insert_id
     */
    public function insert($fields)
    {
        if(!$this->_table){
            throw new Eren_DB_Exception("Table name cant be NULL");
        }
        if(!is_array($fields) || empty($fields)){
            throw new Eren_DB_Exception("The param of insert must be array and cant be NULL");
        }
        
        /*
         * Init FIELDS
         */
        $field = implode(',', array_keys($fields));
        
        /*
         * Init VALUES
         */
        $value = implode("','", array_values($fields));
        $this->_SQL = "INSERT INTO {$this->_table} ({$field}) values ('{$value}')";
        $this->_init();
        return $this->_DB->insert($this->_SQL);
    }
    
    /**
     * Execute UPDATE operation 
     *
     * @param array $fields  The key is the field of table, the value is the value you want to update 
     * @throws Eren_DB_Exception
     * @return bool
     */
    public function update($fields)
    {
        if(!$this->_table){
            throw new Eren_DB_Exception("Table name cant be NULL");
        }
        if(!$this->_where){
            throw new Eren_DB_Exception("Where condition of update opration cant be NULL");
        }
        if(!is_array($fields) || empty($fields)){
            throw new Eren_DB_Exception("The param of update must be array and cant be NULL");
        }
        
        /*
         * Init FIELDS and VALUES 
         */
        foreach ($fields as $key=>$value){
            $temp[] = "{$key} = '{$value}'";
        }
        $values = implode(',', $temp);
        $this->_SQL = "UPDATE {$this->_table} SET $values WHERE {$this->_where}";
        $this->_init();
        return $this->_DB->update($this->_SQL);
    }
    
    /**
     * Execute DELETE operation 
     *
     * @throws Eren_DB_Exception
     * @return delete
     */
    public function delete()
    {
        if(!$this->_table){
            throw new Eren_DB_Exception("Table name cant be NULL");
        }
        if(!$this->_where){
            throw new Eren_DB_Exception("Where condition of delete opration cant be NULL");
        }
        $this->_SQL = "DELETE FROM {$this->_table} WHERE {$this->_where}";
        $this->_init();
        return $this->_DB->delete($this->_SQL);
    }
    
    /**
     * Execute COUNT operation 
     *
     * @throws Eren_DB_Exception
     * @return int
     */
    public function count()
    {
        if(!$this->_table){
            throw new Eren_DB_Exception("Table name cant be NULL", -1);
        }
        $this->_SQL = "SELECT count(*) as total FROM {$this->_table} ";
        if($this->_where){
            $this->_SQL .= "WHERE {$this->_where}";
        }
        $this->_init();
        return $this->_DB->count($this->_SQL);
    }
    
    /**
     * Set WHERE conditioin
     * 
     * @param string $key  The field of the Table
     * @param string $op   The Operators, must in the white list $this->_op
     * @param string $value  The value of the where condition
     * @throws Eren_DB_Exception
     * @return this
     */
    public function where($key, $op, $value)
    {
        if(is_array($key) || empty($key)){
            throw new Eren_DB_Exception("The first param of where must be string and cant be null");
        }
        if(is_array($op) || !isset($this->_op[$op]) || !$this->_op[$op]){
            throw new Eren_DB_Exception("The second param of where cant be supported");
        }
        if(is_array($value)){
            throw new Eren_DB_Exception("The third param of where must be string and cant be null");
        }
        $this->_where .= " {$key} {$op} '{$value}' ";
        return $this;
    }
    
    /**
     * Set leftParenthesis '(' for WHERE condition
     * 
     * @return this
     */
    public function leftParenthesis()
    {
        $this->_where .='(';
        return $this;
    }
    
    /**
     * Set rightParenthesis ')' for WHERE condition
     * 
     * @return this
     */
    public function rightParenthesis()
    {
        $this->_where .=')';
        return $this;
    }
    
    /**
     * Set OR for WHERE condition
     * 
     * @return this
     */
    public function wOR()
    {
        $this->_where .=' OR ';
        return $this;
    }

    /**
     * Set AND for WHERE condition
     * 
     * @return this
     */
    public function wAND()
    {
        $this->_where .=' AND ';
        return $this;
    }
    
    /**
     * Set IN for WHERE condition
     * 
     * @param string $field
     * @param array $values
     * @throws Eren_DB_Exception
     * @return this
     */
    public function wIN($field, $values)
    {
        if(is_array($field) || empty($field)){
            throw new Eren_DB_Exception("The first param of wIN cant be NULL");
        }
        if(!is_array($values) || empty($values)){
            throw new Eren_DB_Exception("The second param of wIN must be Array and cant be NULL");
        }
        $values = implode("','", $values);
        $this->_where .=" {$field} IN ('{$values}') ";
        return $this;
    }
    
    /**
     * Set BETWEEN for WHERE condition
     * 
     * @param string $field
     * @param string $first
     * @param string $second
     * @throws Eren_DB_Exception
     * @return this
     */
    public function wBETWEEN($field, $first, $second)
    {
        if(is_array($field) || empty($field) || 
           is_array($first) || empty($first) || 
           is_array($second) || empty($second)){
            throw new Eren_DB_Exception("Every param of wBETWEEN must be Array and cant be NULL");
        }
        $this->_where .=" {$field} BETWEEN '{$first}' AND '{$second}' ";
        return $this;
    }
    
    /**
     * Set LIMIT condition
     * 
     * @param int $start
     * @param int $limit
     * @throws Eren_DB_Exception
     * @return this
     */
    public function limit($start, $limit)
    {
        if(!is_numeric($start) || $start < 0){
            throw new Eren_DB_Exception("The first param of limit must be int and >=0");
        }
        if(!is_numeric($limit) || $limit <= 0){
            throw new Eren_DB_Exception("The second param of limit must be int and >0");
        }
        $this->_limit .= " {$start}, {$limit} ";
        return $this;
    }
    
    /**
     * Set ORDER BY condition
     * 
     * @param string $field
     * @param string $order
     * @throws Eren_DB_Exception
     * @return this
     */
    public function orderBy($field, $order = 'ASC')
    {
        if(is_array($field) || empty($field)){
            throw new Eren_DB_Exception("The first param of wIN cant be Array or NULL");
        }
        if(is_array($order) || ($order != 'ASC' && $order != 'DESC')){
            throw new Eren_DB_Exception("The second param must be 'ASC' or 'DESC'");
        }
        if(!$this->_order){
            $this->_order .= " {$field} {$order} ";
        }else{
            $this->_order .= ", {$field} {$order} ";
        }
        return $this;
    }
    
    /**
     * Get the SQL
     * 
     * @return string
     */
    public function getSQL()
    {
        return $this->_SQL;
    }
}